/// <reference path="tfcore.js"/>
/// <reference path="edNLog.js"/>
// #pragma NoCompStart
/*
edcblist.js  1.0
tfComboListBox javascript functions
*/
// #pragma NoCompEnd

/*******************************************************
	Global functions
********************************************************/
	// duration of the display of the red error message in the checkbox list
	var ERROR_DISPLAY_TIMEOUT = 1000; 
	// color of the error message in the checkbox list
	var ERROR_DISPLAY_COLOR = "red";
	// font weight of the error message in the checkbox list
	var ERROR_DISPLAY_FONT_WEIGHT = "bold";


	/*******************************************************
		Utilities that help navigate through the DOM
	********************************************************/
	// Returns the first with tagName = 'label' child element found under 
	// the specified root element (might be the root itself). This search
	// is case insensitive 
	function getItemLabel(rootElem) {
		if (rootElem.tagName.toLowerCase() == 'label') {
			return rootElem;
		} else {
			for (var i=0; i<rootElem.childNodes.length; i++) {
				var childElement = rootElem.childNodes[i];
				var childReturn  = getItemLabel(childElement);
				if (childReturn != null) {
					return childReturn;
				}
			}
			
			return null;
		}
	}
	
/*******************************************************
	This is a simple wrapper that allows us to clear the
	red warning message from an item when the number
	of maximum selected items has been reached.
********************************************************/
	function ClickedCheckBox(labelElement, item) {
		this.labelElement = labelElement;
		this.item = item;
	}
	
/*******************************************************
	Various characteristics and behaviors of 
	the combo box image
********************************************************/
	function DropDownButton(imageDivId, imageUpSrc, imageDownSrc, imageOverSrc, imageSize) {	
		this.imageDiv = xGetElementById(imageDivId);
	
		this.imageUpSrc = imageUpSrc;
		this.imageDownSrc = imageDownSrc;
		this.imageOverSrc = imageOverSrc;
		this.imageSize = imageSize;

		this.image = document.createElement('img');
		this.image.width = imageSize;
		this.image.height = imageSize;
		this.image.src = imageUpSrc;
		
		// add the image to the image div
		this.imageDiv.appendChild(this.image);
	}
	
	DropDownButton.prototype.showMouseUp = function() {
		this.image.src = this.imageUpSrc;
	}

	DropDownButton.prototype.showMouseDown = function() {
		this.image.src = this.imageDownSrc;
	}

	DropDownButton.prototype.showMouseOver = function() {
		this.image.src = this.imageOverSrc;
	}

/*******************************************************
	tfCBList class
********************************************************/

	function tfCBList( name, maxSelectionSize, selectionTextUpdateFunction , checkBoxList, dropDownButton) {
		this.name = name;
		this.maxSelectionSize = maxSelectionSize;
		this.selectionTextFormatter = selectionTextUpdateFunction;
		this.checkBoxList = checkBoxList;

		// This queue enable us to restore the state of clicked checkboxes even if 
		// several checkboxes are clicked before we restore the state (i.e. before
		// the timeout ERROR_DISPLAY_TIMEOUT). Each time a timeout expires, we pop
		// the first element from this queue (of type ClickedCheckBox) and we restore the state of the
		// encapsulated label.
		this.clickedCheckBoxes = [];
		
		// Get a reference to the embedded divs
		this.mainDiv = xGetElementById(name + ":Div");
		this.comboBoxDiv = xGetElementById(name + "_combobox");
		this.comboTextDiv = xGetElementById(name + "_comboboxtext");
		this.checkBoxListDiv = xGetElementById(name + "_checkboxlist");
		this.iFrameHackID = name + "_iframehack";
		
		// set the combo box image state
		this.dropDownButton = dropDownButton;
		
		// Define mouse events for the drop down list 
		this.mouseOverHandler = function(CallerObject) {
			return function() {
				CallerObject.dropDownButton.showMouseOver();
				CallerObject.comboBoxDiv.className = "comboBoxFocus";
			}
		} (this);
		
		this.mouseOutHandler = function(CallerObject) {
			return function() {
				CallerObject.dropDownButton.showMouseUp();
				CallerObject.comboBoxDiv.className = "comboBox";
			}
		} (this);

		this.mouseDownHandler = function(CallerObject) {
			return function() {
				CallerObject.dropDownButton.showMouseDown();
			}
		} (this);

		this.mouseUpHandler = function(CallerObject) {
			return function() {
				dropDownBtn.showOver();

				if (is.ie)
				{
					window.event.cancelBubble = true;
				} else {
					e.stopPropagation();
				}

				CallerObject.toggleCheckBoxListVisibility();
			}
		} (this);
					
		this.documentClickHandler = function (CallerObject)
		{
			return function(mevent)
			{
				src = is.ie ? window.event.srcElement : mevent.srcElement;
				if (!src) src = is.ie ? window.event.target : mevent.target;

				var isClickInsideMainDiv = isElementInside(src, CallerObject.mainDiv)
				if (isClickInsideMainDiv)
				{
					// if the user clicked the combo box, then switch the visibility of the checkbox list
					var isClickInsideCombo = isElementInside(src, CallerObject.comboBoxDiv);
					if (isClickInsideCombo) {
						CallerObject.toggleCheckBoxListVisibility();
					}
					else {
						// else means that the user clicked inside the checkbox list, so we won't do anything
						// so that the user are able to continue to select of other items in the list
					}
				} 
				else {
					CallerObject.hideCheckBoxList();
				}
			}

		} (this);
		
		this.keyPressedHandler = function(CallerObject) 
		{
			return function( e ) {
				if ( e == null ) e = window.event;

				var code = e.keyCode ? e.keyCode : e.which;

				if ( code == 9 ) { // tab arrow pressed
						// Hide the checkboxlist, the user wants to navigate away from it
						CallerObject.hideCheckBoxList();
						return true;
				}
				else if ( code == 38 ) { // up arrow pressed
						// hide the checkboxlist
						CallerObject.hideCheckBoxList();
						return false;
				}
				else if ( code == 40 ) { // down arrow pressed
						// Show the checkboxlist
						CallerObject.showCheckBoxList();
						return false;
				}
				
				return true;
			}
		} (this);

		// Attach javascripts mouse events to the comboBoxDiv
		//this.dropDownButton.image.onmouseover = this.mouseOverHandler;
		//this.dropDownButton.image.onmouseout = this.mouseOutHandler;
		//this.dropDownButton.image.onmousedown = this.mouseDownHandler;
		//this.dropDownButton.onclick = this.mouseUpHandler;

		this.comboBoxDiv.onmouseover = this.mouseOverHandler;
		this.comboBoxDiv.onmouseout = this.mouseOutHandler;
		this.comboBoxDiv.onmousedown = this.mouseDownHandler;
		this.comboBoxDiv.onkeydown = this.keyPressedHandler;

		// attach the click handler to the document, so that we can manage exterior clicks
		if (is.ie) {
			document.body.attachEvent("onclick", this.documentClickHandler);
		} 
		else {
			window.addEventListener("click", this.documentClickHandler, false);
		}

		if (this.isCheckBoxListVisible()) {
			this.toggleCheckBoxListVisibility();
		}
	}
	
	tfCBList.prototype.handleLegalSelection = function (item, newText) {
		this.comboTextDiv.innerHTML = newText;
		
		// First level items do not need to update their folder because they have none
		if (item.folder != null)
			this.updateFolderFace(item.folder);
	}
	
	tfCBList.prototype.updateAllFoldersFace = function() {
		for (var i = 0; i < this.checkBoxList.folders.length; i++) {
			var folder = this.checkBoxList.folders[i];
			this.updateFolderFace(folder);
		}
	}
	
	tfCBList.prototype.updateFolderFace = function(folder) {
		// update the style of the parent folder if this item is the first selected item
		// in that folder.
		var parentFolderDivs = this.getFolderDivs(folder);

		for (var i = 0; i < parentFolderDivs.length; i++) {
			var parentFolderDiv = parentFolderDivs[i];
			
			var selectedItemsCount = this.getSelectedItemsCount(folder);
			if (selectedItemsCount == 0) {
				// restore the initial properties if no items are selected
				parentFolderDiv.className = "folder";
			}
			else {
				// emphasize the folders with non empty selection
				parentFolderDiv.className = "folderSelected";
			}
		}
	}
	
	tfCBList.prototype.getFolderDivs = function(folder) {
		var result = [];
		
		result.push(xGetElementById(folder.id + "-close"));
		result.push(xGetElementById(folder.id + "-open"));
		
		return result;
	}
	
	tfCBList.prototype.getSelectedItemsCount = function(folder) {
		var result = 0;

		for (var i = 0; i < folder.items.length; i++) {
			var currentItem = folder.items[i];
			if (currentItem.checkBox.checked) {
				result++;
			}
		}
		
		return result;
	}
	
	// Returns the enclosing div of an item
	tfCBList.prototype.getItemOuterDiv = function(item) {
		return xGetElementById(item.id).parentNode;
	}
	
	tfCBList.prototype.handleSelectionOverflow = function(item, warningText) {
		if(this.maxSelectionSize == 1)
		{
			for (var i = 0; i < this.checkBoxList.items.length; i++) {
				var itemP = this.checkBoxList.items[i];
				itemP.select(false);
				itemP.checkBox.checked = false;
			}
			item.select(true);
			item.checkBox.checked = true;
		}
		else
		{
			// don't allow more items than maximum selection size
			item.checkBox.checked = false;
		
			// store the label and the item that have been clicked so that we are
			// able to restore their properties when the timeout expires
			if (this.clickedCheckBoxes.length == 0) {
				var outerDivElem = this.getItemOuterDiv(item);
				var clickedLabelElement = getItemLabel(outerDivElem);
				
				var clickedItem = item;
				var clickedCheckBox = new ClickedCheckBox(clickedLabelElement, clickedItem);
				this.clickedCheckBoxes.push(clickedCheckBox); 

				clickedCheckBox.labelElement.style.color = ERROR_DISPLAY_COLOR;
				clickedCheckBox.labelElement.style.fontWeight = ERROR_DISPLAY_FONT_WEIGHT;
				clickedCheckBox.labelElement.innerHTML = warningText;			
			}
		}
	}
	
	function clearError (ddcblName) {
		var ddcbl = eval(ddcblName);
		
		if (ddcbl.clickedCheckBoxes.length > 0) {
			// pop the first checkbox that has been clicked
			var clickedCheckBox = ddcbl.clickedCheckBoxes.pop();
			
			clickedCheckBox.labelElement.style.color = "";
			clickedCheckBox.labelElement.style.fontWeight = "";
			clickedCheckBox.labelElement.innerHTML = clickedCheckBox.item.name;
		}
	}

	tfCBList.prototype.isCheckBoxListVisible = function() {
		return this.checkBoxListDiv.style.visibility == "hidden" ? false : true;
	}
	
	tfCBList.prototype.toggleCheckBoxListVisibility = function() {
		if (this.isCheckBoxListVisible())
		{
			this.hideCheckBoxList();
		} else {
			this.showCheckBoxList();
		}
	}
	
	tfCBList.prototype.showCheckBoxList = function() {
		// show the iframe behind first
		var iFrameHack = xGetElementById(this.iFrameHackID);
		if (iFrameHack != null) {
			iFrameHack.style.display = "block";
		}

		// show the checkbox list
		this.checkBoxListDiv.style.visibility = "visible";
		this.checkBoxListDiv.style.display = "block";
	}

	tfCBList.prototype.hideCheckBoxList = function() {
		// hide the iframe behind first
		var iFrameHack = xGetElementById(this.iFrameHackID);
		if (iFrameHack != null) {
			iFrameHack.style.display = "none";
		}
		
		// hide the checkbox list
		this.checkBoxListDiv.style.display = "none";
		this.checkBoxListDiv.style.visibility = "hidden";
	}
	
	tfCBList.prototype.unselectAll = function() {
		// unselect top level items (if any)
		for (var i = 0; i < this.checkBoxList.items.length; i++) {
			var item = this.checkBoxList.items[i];
			item.select(false);
		}
		
		// unselect items whithin folders (if any)
		for (var i = 0; i < this.checkBoxList.folders.length; i++) {
			var folder = this.checkBoxList.folders[i];
			for (var j = 0; j < folder.items.length; j++) {
				var item = folder.items[j];
				item.select(false);
			}
		}
	}
	
	tfCBList.prototype.close = function() {
		this.hideCheckBoxList();
	}

	/*******************************************************
		This function updates the selection description of the specified
		tfCBList object and also acts as a validator 
		for the CheckBoxList. When the maximum selection is reached,
		the user is prevented from selecting new messages and 
		an error is shown.
	********************************************************/
	function updateSelection(item, ddcbl) {
		if (item != null) {
			var selectionSize = ddcbl.checkBoxList.getSelection().itemCount;
			var maxSelectionSize = ddcbl.maxSelectionSize;
			// legal selection has been performed, update the selection description 
			var newSelectionText = ddcbl.selectionTextFormatter(ddcbl);
			if (selectionSize <= maxSelectionSize) {
				ddcbl.handleLegalSelection(item, newSelectionText);
			}
			else {								
				// display a red error message
				ddcbl.handleSelectionOverflow(item, newSelectionText);
				
				// and clear it when the timeout expires
				window.setTimeout("clearError" + "('" + ddcbl.name + "')", ERROR_DISPLAY_TIMEOUT);
			}
		}
	}

